/*
 * Copyright (c) China Telecom Cloud Technology Co., Ltd. 2024-2025. All rights reserved.
 */
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

#include "../../3FS/src/lib/api/hf3fs_usrbio.h"
#include "../include/KVStorageClient.h"
#include "../include/ThreeFsStrategy.h"
#include "Common.h"
#include <filesystem>
#include <sys/mman.h>
#include <sys/stat.h>

struct UsrbioGuard {
    struct hf3fs_ior *ior;
    int32_t fd;
    UsrbioGuard() : fd(-1) {}
    ~UsrbioGuard() {
        if (fd >= 0) {
            hf3fs_dereg_fd(fd);
            close(fd);
        }
    }
};

ThreeFsStartegy::~ThreeFsStartegy() {
    if (inited) {
        hf3fs_iovdestroy(&iov);
    }
}

int32_t ThreeFsStartegy::createIov() noexcept {
    int32_t ret = hf3fs_iovcreate(&iov, fsMountPath.c_str(), shmSize, 0, -1,
                                  shmPath.c_str(), shmAddr);
    if (ret != 0) {
        LOG_ERROR("create iov failed");
        return ret;
    }
    inited = true;
    return KVC_OK;
}

hf3fs_iov *ThreeFsStartegy::getIov() noexcept {
    if (!inited) {
        return nullptr;
    }
    return &iov;
}

int32_t ThreeFsStartegy::usrbioReadWrite(
    const Segment *segment, const uint64_t srcOffset, const std::string &objId,
    const uint64_t tgtFileOff, const uint64_t ioSize, const bool read) {
    if (!inited) {
        LOG_ERROR("iov didn't initialized");
        return KVC_ERR_INTERNAL;
    }
    int32_t ret = KVC_OK;
    int32_t idx = -1;
    hf3fs_cqe cqes[1];
    std::string fatherPath = getFileDirPath(objId);
    const char *tgtPath = getFileFullPath(objId).c_str();
    std::string shmName = shmPath.substr(SHM_PATH_LEN);
    UsrbioGuard usrbio;
    usrbio.ior =
        read == true ? getReadIor(fsMountPath) : getWriteIor(fsMountPath);
    if (usrbio.ior == nullptr) {
        return KVC_ERR_INTERNAL;
    }
    if (read) {
        usrbio.fd = open(tgtPath, O_RDONLY);
    } else {
        usrbio.fd =
            createFileWithPath(tgtPath, O_RDWR | O_CREAT, USER_READ_WRITE);
    }
    if (usrbio.fd < 0) {
        LOG_ERROR("open tgt_file failed");
        return KVC_ERR_INTERNAL;
    }
    ret = hf3fs_reg_fd(usrbio.fd, 0);
    if (ret > 0) {
        return KVC_ERR_INTERNAL;
    }
    idx = hf3fs_prep_io(usrbio.ior, getIov(), read,
                        getIov()->base + segment->getOffset() + srcOffset,
                        usrbio.fd, tgtFileOff, ioSize, tgtPath);
    if (idx < 0) {
        LOG_ERROR("hf3fs_prep_io failed");
        return KVC_ERR_INTERNAL;
    }
    ret = hf3fs_submit_ios(usrbio.ior);
    if (ret != 0) {
        LOG_ERROR("hf3fs_submit_ios failed");
        return KVC_ERR_INTERNAL;
    }
    idx = hf3fs_wait_for_ios(usrbio.ior, cqes, 1, 1, nullptr);
    if (idx < 0) {
        LOG_ERROR("hf3fs_wait_for_ios failed");
        return KVC_ERR_INTERNAL;
    }
    if (cqes[0].result != ioSize) {
        LOG_ERROR("iosize mismatch cqe result");
        return KVC_ERR_INTERNAL;
    }
    return KVC_OK;
}

int32_t ThreeFsStartegy::get(std::string &objId, const Segment &segment,
                             const uint64_t srcOffset, const uint64_t dstOffset,
                             const uint64_t length) noexcept {
    int32_t ret =
        usrbioReadWrite(&segment, srcOffset, objId, dstOffset, length, true);
    if (ret != KVC_OK) {
        LOG_ERROR("3FS get data failed");
    }

    return ret;
}

int32_t ThreeFsStartegy::put(std::string &objId, const Segment &segment,
                             const uint64_t srcOffset, const uint64_t dstOffset,
                             const uint64_t length) noexcept {
    int32_t ret =
        usrbioReadWrite(&segment, srcOffset, objId, dstOffset, length, false);
    if (ret != KVC_OK) {
        LOG_ERROR("3FS put data failed");
    }

    return ret;
}

int32_t ThreeFsStartegy::remove(std::string &objId) noexcept {
    auto objPath = getFileFullPath(objId);
    if (!fs::remove(objPath)) {
        LOG_ERROR("unlink file failed");
        return KVC_ERR_INTERNAL;
    }
    return KVC_OK;
}

bool ThreeFsStartegy::isExist(std::string &objId) noexcept {
    auto objPath = getFileFullPath(objId);

    struct stat info;
    return (::stat(objPath.c_str(), &info) == 0);
}

struct FdGuard {
    int32_t fd;
    FdGuard(int32_t newFd) : fd(newFd) {}
    ~FdGuard() {
        if (fd >= 0) {
            close(fd);
        }
    }
};

hf3fs_ior *ThreeFsStartegy::getReadIor(std::string fsMountPath) {
    if (readIor.get() == nullptr) {
        std::lock_guard<std::mutex> lock(readIorMutex);
        if (readIor.get() == nullptr) {
            readIor.reset(new struct hf3fs_ior);
            int32_t ret = hf3fs_iorcreate4(readIor.get(), fsMountPath.c_str(),
                                           1, 1, 0, 0, -1, 0);
            if (ret != 0) {
                readIor.reset();
                LOG_ERROR("read ior create failed");
            }
        }
    }
    return readIor.get();
}

hf3fs_ior *ThreeFsStartegy::getWriteIor(std::string fsMountPath) {
    if (writeIor.get() == nullptr) {
        std::lock_guard<std::mutex> lock(writeIorMutex);
        if (writeIor.get() == nullptr) {
            writeIor.reset(new struct hf3fs_ior);
            int32_t ret = hf3fs_iorcreate4(writeIor.get(), fsMountPath.c_str(),
                                           1, 0, 0, 0, -1, 0);
            if (ret != 0) {
                writeIor.reset();
                LOG_ERROR("write ior create failed");
            }
        }
    }
    return writeIor.get();
}